package main

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"gateway/setting"
	"gateway/utils"
	"io/fs"
	"net"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	"github.com/fsnotify/fsnotify"
)

const (
	maxRetries        = 3                // 最大重试次数
	chunkTimeout      = 5 * time.Second  // 分片发送超时时间
	maxChunkSize      = 60000            // 每个分片的最大大小
	syncTimeout       = 30 * time.Second // 同步超时时间
	heartbeatInterval = 2 * time.Second  // 心跳包发送间隔
	maxMessageSize    = 65507            // UDP 报文最大大小
)

// keepAliveConfig 定义网络配置参数
type keepAliveConfig struct {
	IPAddress string `json:"ip_address"` // 本机 IP 地址
	Port      string `json:"port"`       // 监听端口
	PeerIP    string `json:"peer_ip"`    // 对端 IP 地址
}

// FileChunk 定义文件分片结构
type FileChunk struct {
	FileName    string `json:"file_name"`    // 文件名
	ChunkIndex  int    `json:"chunk_index"`  // 分片索引
	TotalChunks int    `json:"total_chunks"` // 总分片数
	Data        []byte `json:"data"`         // 分片数据
	Checksum    string `json:"checksum"`     // 分片校验和
}

// ChunkAck 定义分片确认结构
type ChunkAck struct {
	FileName   string `json:"file_name"`   // 文件名
	ChunkIndex int    `json:"chunk_index"` // 分片索引
	Success    bool   `json:"success"`     // 是否成功
}

// Message 定义消息结构
type Message struct {
	Type    string      `json:"type"`    // 消息类型（heartbeat/file_chunk/chunk_ack）
	Content interface{} `json:"content"` // 消息内容
}

// metric 定义统计指标
type metric struct {
	heartbeatsSent     int64     // 心跳包发送次数
	heartbeatsReceived int64     // 心跳包接收次数
	syncAttempts       int64     // 同步尝试次数
	syncSuccess        int64     // 同步成功次数
	syncFailed         int64     // 同步失败次数
	chunksSent         int64     // 分片发送次数
	chunksReceived     int64     // 分片接收次数
	lastSyncTime       time.Time // 最后同步时间
}

var (
	keepAlive  keepAliveConfig               // 网络配置
	active     bool                          // 节点是否激活
	mu         sync.RWMutex                  // 读写锁
	metrics    metric                        // 统计指标
	chunkCache map[string]map[int]*FileChunk // 分片缓存
	retryQueue chan *FileChunk               // 重试队列
	chunkAcks  chan ChunkAck                 // 分片确认队列
)

// init 初始化全局变量
func init() {
	chunkCache = make(map[string]map[int]*FileChunk)
	retryQueue = make(chan *FileChunk, 1000)
	chunkAcks = make(chan ChunkAck, 1000)
}

// calculateChecksum 计算数据的 MD5 校验和
func calculateChecksum(data []byte) string {
	hash := md5.New()
	hash.Write(data)
	return hex.EncodeToString(hash.Sum(nil))
}

// ReadKeepAliveParamFromJson 从 JSON 文件读取网络配置
func ReadKeepAliveParamFromJson() bool {
	data, err := utils.FileRead("./config/keepAlive.json")
	if err != nil {
		if strings.Contains(err.Error(), "no such file or directory") {
			setting.ZAPS.Debug("打开 keepAlive 配置 JSON 文件失败")
		} else {
			setting.ZAPS.Errorf("打开 keepAlive 配置 JSON 文件失败: %v", err)
		}
		return false
	}

	err = json.Unmarshal(data, &keepAlive)
	if err != nil {
		setting.ZAPS.Errorf("解析 keepAlive 配置 JSON 文件失败: %v", err)
		return false
	}
	setting.ZAPS.Debugf("成功读取 keepAlive 配置 JSON 文件")
	return true
}

// WriteKeepAliveConfigParamToJson 将网络配置写入 JSON 文件
func WriteKeepAliveConfigParamToJson(params keepAliveConfig) {
	utils.DirIsExist("./config")
	sJson, _ := json.Marshal(params)
	err := utils.FileWrite("./config/keepAlive.json", sJson)
	if err != nil {
		setting.ZAPS.Warnf("写入 keepAlive 配置 JSON 文件失败: %v", err)
		return
	}
	setting.ZAPS.Info("成功写入 keepAlive 配置 JSON 文件")
}

// startHeartbeat 启动心跳包发送
func startHeartbeat() {
	backoff := time.Second
	for {
		err := sendHeartbeat()
		if err != nil {
			setting.ZAPS.Errorf("发送心跳包失败: %v", err)
			handleFailure()
			time.Sleep(backoff)
			if backoff < 10*time.Second {
				backoff *= 2
			}
			continue
		}
		backoff = time.Second
		atomic.AddInt64(&metrics.heartbeatsSent, 1)
		time.Sleep(heartbeatInterval)
	}
}

// sendHeartbeat 发送心跳包
func sendHeartbeat() error {
	addr, err := net.ResolveUDPAddr("udp", keepAlive.PeerIP+":"+keepAlive.Port)
	if err != nil {
		return err
	}
	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return err
	}
	defer conn.Close()

	message := Message{Type: "heartbeat", Content: ""}
	data, _ := json.Marshal(message)
	_, err = conn.Write(data)
	return err
}

// handleFailure 处理失败情况
func handleFailure() {
	mu.Lock()
	if !active {
		active = true
		setting.ZAPS.Info("节点激活，开始同步配置")
		go syncConfig()
		go monitorSelfpara()
	}
	mu.Unlock()
}

// syncConfig 同步配置
func syncConfig() {
	atomic.AddInt64(&metrics.syncAttempts, 1)
	metrics.lastSyncTime = time.Now()

	addr, err := net.ResolveUDPAddr("udp", keepAlive.PeerIP+":"+keepAlive.Port)
	if err != nil {
		setting.ZAPS.Errorf("解析 UDP 地址失败: %v", err)
		return
	}

	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		setting.ZAPS.Errorf("建立 UDP 连接失败: %v", err)
		return
	}
	defer conn.Close()

	syncDone := make(chan bool)
	go func() {
		if err := syncFiles(conn); err != nil {
			atomic.AddInt64(&metrics.syncFailed, 1)
			setting.ZAPS.Errorf("同步失败: %v", err)
			syncDone <- false
			return
		}
		atomic.AddInt64(&metrics.syncSuccess, 1)
		syncDone <- true
	}()

	select {
	case success := <-syncDone:
		if success {
			setting.ZAPS.Info("同步完成")
		}
	case <-time.After(syncTimeout):
		setting.ZAPS.Error("同步超时")
		atomic.AddInt64(&metrics.syncFailed, 1)
	}
}

// syncFiles 同步文件
func syncFiles(conn *net.UDPConn) error {
	files, err := os.ReadDir("./selfpara")
	if err != nil {
		return err
	}

	for _, file := range files {
		if file.Name() == "networkConfigParams.json" {
			continue
		}

		if err := sendFile(conn, file.Name()); err != nil {
			return err
		}
	}
	return nil
}

// sendFile 发送文件
func sendFile(conn *net.UDPConn, fileName string) error {
	fileData, err := os.ReadFile(filepath.Join("./selfpara", fileName))
	if err != nil {
		return err
	}

	totalChunks := (len(fileData) + maxChunkSize - 1) / maxChunkSize
	for i := 0; i < totalChunks; i++ {
		start := i * maxChunkSize
		end := start + maxChunkSize
		if end > len(fileData) {
			end = len(fileData)
		}

		chunk := &FileChunk{
			FileName:    fileName,
			ChunkIndex:  i,
			TotalChunks: totalChunks,
			Data:        fileData[start:end],
		}

		if err := sendFileChunk(conn, chunk); err != nil {
			retryQueue <- chunk
			continue
		}
	}

	return nil
}

// sendFileChunk 发送文件分片
func sendFileChunk(conn *net.UDPConn, chunk *FileChunk) error {
	chunk.Checksum = calculateChecksum(chunk.Data)
	message := Message{
		Type:    "file_chunk",
		Content: chunk,
	}

	for retry := 0; retry < maxRetries; retry++ {
		data, _ := json.Marshal(message)
		_, err := conn.Write(data)
		if err != nil {
			continue
		}

		select {
		case ack := <-chunkAcks:
			if ack.FileName == chunk.FileName && ack.ChunkIndex == chunk.ChunkIndex && ack.Success {
				atomic.AddInt64(&metrics.chunksSent, 1)
				return nil
			}
		case <-time.After(chunkTimeout):
			setting.ZAPS.Warnf("分片发送超时重试: %s-%d", chunk.FileName, chunk.ChunkIndex)
		}
	}

	return errors.New("发送分片失败")
}

// startListener 启动 UDP 监听
func startListener() {
	addr, err := net.ResolveUDPAddr("udp", ":"+keepAlive.Port)
	if err != nil {
		setting.ZAPS.Errorf("解析 UDP 地址失败: %v", err)
		return
	}

	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		setting.ZAPS.Errorf("启动 UDP 监听失败: %v", err)
		return
	}
	defer conn.Close()

	buf := make([]byte, maxMessageSize)
	for {
		n, remoteAddr, err := conn.ReadFromUDP(buf)
		if err != nil {
			setting.ZAPS.Errorf("读取 UDP 数据失败: %v", err)
			continue
		}

		var message Message
		if err := json.Unmarshal(buf[:n], &message); err != nil {
			setting.ZAPS.Errorf("解析消息失败: %v", err)
			continue
		}

		go handleMessage(message, conn, remoteAddr)
	}
}

// handleMessage 处理接收到的消息
func handleMessage(message Message, conn *net.UDPConn, remoteAddr *net.UDPAddr) {
	switch message.Type {
	case "heartbeat":
		atomic.AddInt64(&metrics.heartbeatsReceived, 1)
		setting.ZAPS.Debug("收到心跳包")
		sendHeartbeatAck(conn, remoteAddr)

	case "file_chunk":
		content, _ := json.Marshal(message.Content)
		var chunk FileChunk
		if err := json.Unmarshal(content, &chunk); err != nil {
			setting.ZAPS.Errorf("解析分片失败: %v", err)
			return
		}

		if calculateChecksum(chunk.Data) != chunk.Checksum {
			setting.ZAPS.Errorf("分片校验失败: %s-%d", chunk.FileName, chunk.ChunkIndex)
			sendChunkAck(conn, remoteAddr, chunk.FileName, chunk.ChunkIndex, false)
			return
		}

		atomic.AddInt64(&metrics.chunksReceived, 1)
		if err := processChunk(chunk); err != nil {
			setting.ZAPS.Errorf("处理分片失败: %v", err)
			sendChunkAck(conn, remoteAddr, chunk.FileName, chunk.ChunkIndex, false)
			return
		}

		sendChunkAck(conn, remoteAddr, chunk.FileName, chunk.ChunkIndex, true)
	}
}

// processChunk 处理文件分片
func processChunk(chunk FileChunk) error {
	mu.Lock()
	defer mu.Unlock()

	if _, exists := chunkCache[chunk.FileName]; !exists {
		chunkCache[chunk.FileName] = make(map[int]*FileChunk)
	}

	chunkCache[chunk.FileName][chunk.ChunkIndex] = &chunk

	if len(chunkCache[chunk.FileName]) == chunk.TotalChunks {
		if err := assembleFile(chunk.FileName); err != nil {
			return err
		}
		delete(chunkCache, chunk.FileName)
	}

	return nil
}

// assembleFile 组装文件
func assembleFile(fileName string) error {
	chunks := chunkCache[fileName]
	fileData := make([]byte, 0)

	for i := 0; i < len(chunks); i++ {
		if chunk, exists := chunks[i]; exists {
			fileData = append(fileData, chunk.Data...)
		} else {
			return fmt.Errorf("缺少分片: %d", i)
		}
	}

	return os.WriteFile(filepath.Join("./selfpara", fileName), fileData, fs.ModePerm)
}

// sendChunkAck 发送分片确认
func sendChunkAck(conn *net.UDPConn, addr *net.UDPAddr, fileName string, chunkIndex int, success bool) {
	ack := ChunkAck{
		FileName:   fileName,
		ChunkIndex: chunkIndex,
		Success:    success,
	}

	message := Message{
		Type:    "chunk_ack",
		Content: ack,
	}

	data, _ := json.Marshal(message)
	conn.WriteToUDP(data, addr)
}

// GetMetrics 获取统计指标
func GetMetrics() metric {
	return metrics
}

// ResetMetrics 重置统计指标
func ResetMetrics() {
	atomic.StoreInt64(&metrics.heartbeatsSent, 0)
	atomic.StoreInt64(&metrics.heartbeatsReceived, 0)
	atomic.StoreInt64(&metrics.syncAttempts, 0)
	atomic.StoreInt64(&metrics.syncSuccess, 0)
	atomic.StoreInt64(&metrics.syncFailed, 0)
	atomic.StoreInt64(&metrics.chunksSent, 0)
	atomic.StoreInt64(&metrics.chunksReceived, 0)
}

// Start 启动服务
func Start() {
	if !ReadKeepAliveParamFromJson() {
		setting.ZAPS.Error("读取配置失败，服务启动失败")
		return
	}

	go startListener()

	time.Sleep(10 * time.Second)

	if atomic.LoadInt64(&metrics.heartbeatsReceived) == 0 {
		setting.ZAPS.Info("未收到心跳包，切换为主机模式")
		go startHeartbeat()
		go processRetryQueue()
		go monitorSelfpara()
	} else {
		setting.ZAPS.Info("收到心跳包，保持从机模式")
	}
}

// processRetryQueue 处理重试队列
func processRetryQueue() {
	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()

	for {
		select {
		case chunk := <-retryQueue:
			addr, err := net.ResolveUDPAddr("udp", keepAlive.PeerIP+":"+keepAlive.Port)
			if err != nil {
				setting.ZAPS.Errorf("解析 UDP 地址失败: %v", err)
				continue
			}

			conn, err := net.DialUDP("udp", nil, addr)
			if err != nil {
				setting.ZAPS.Errorf("建立 UDP 连接失败: %v", err)
				continue
			}

			if err := sendFileChunk(conn, chunk); err != nil {
				setting.ZAPS.Errorf("重试发送分片失败: %v", err)
			}
			conn.Close()

		case <-ticker.C:
			mu.Lock()
			// 清理过期的分片缓存
			for fileName, chunks := range chunkCache {
				if len(chunks) > 0 && time.Since(metrics.lastSyncTime) > syncTimeout {
					delete(chunkCache, fileName)
					setting.ZAPS.Warnf("清理过期文件分片: %s", fileName)
				}
			}
			mu.Unlock()
		}
	}
}

// monitorSelfpara 监控 selfpara 文件夹的变化
func monitorSelfpara() {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		setting.ZAPS.Errorf("创建文件监视器失败: %v", err)
		return
	}
	defer watcher.Close()

	err = watcher.Add("./selfpara")
	if err != nil {
		setting.ZAPS.Errorf("添加监视路径失败: %v", err)
		return
	}

	for {
		select {
		case event := <-watcher.Events:
			if event.Op&fsnotify.Write == fsnotify.Write {
				setting.ZAPS.Infof("检测到文件变化: %s", event.Name)
				go syncConfig()
			}
		case err := <-watcher.Errors:
			setting.ZAPS.Errorf("文件监视器错误: %v", err)
		}
	}
}

// GetSyncStatus 获取同步状态
func GetSyncStatus() string {
	syncRate := float64(0)
	if metrics.syncAttempts > 0 {
		syncRate = float64(metrics.syncSuccess) / float64(metrics.syncAttempts) * 100
	}

	return fmt.Sprintf(
		"同步状态:\n"+
			"心跳发送: %d\n"+
			"心跳接收: %d\n"+
			"同步尝试: %d\n"+
			"同步成功: %d\n"+
			"同步失败: %d\n"+
			"同步成功率: %.2f%%\n"+
			"分片发送: %d\n"+
			"分片接收: %d\n"+
			"最后同步时间: %s",
		metrics.heartbeatsSent,
		metrics.heartbeatsReceived,
		metrics.syncAttempts,
		metrics.syncSuccess,
		metrics.syncFailed,
		syncRate,
		metrics.chunksSent,
		metrics.chunksReceived,
		metrics.lastSyncTime.Format("2006-01-02 15:04:05"),
	)
}

// 清理资源
func Cleanup() {
	mu.Lock()
	defer mu.Unlock()

	// 清理分片缓存
	for fileName := range chunkCache {
		delete(chunkCache, fileName)
	}

	// 清空重试队列
	for len(retryQueue) > 0 {
		<-retryQueue
	}

	// 重置指标
	ResetMetrics()
}

// 检查是否处于活动状态
func IsActive() bool {
	mu.RLock()
	defer mu.RUnlock()
	return active
}

// 获取当前配置
func GetConfig() keepAliveConfig {
	mu.RLock()
	defer mu.RUnlock()
	return keepAlive
}

// 更新配置
func UpdateConfig(config keepAliveConfig) error {
	if config.IPAddress == "" || config.Port == "" || config.PeerIP == "" {
		return errors.New("无效的配置参数")
	}

	mu.Lock()
	keepAlive = config
	mu.Unlock()

	WriteKeepAliveConfigParamToJson(config)
	return nil
}

// sendHeartbeatAck 发送心跳包确认
func sendHeartbeatAck(conn *net.UDPConn, addr *net.UDPAddr) {
	message := Message{Type: "heartbeat_ack", Content: "OK"}
	data, _ := json.Marshal(message)
	conn.WriteToUDP(data, addr)
}
